package com.gsta.ifishery.netty.handler;

import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gsta.ifishery.common.util.DateUtils;
import com.gsta.ifishery.common.util.NettyConfigUtils;
import com.gsta.ifishery.common.util.NettyServerUtils;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;

/**
 * 连接检测 . <br>
 * 
 * @author hkb
 */
@Sharable
public class ConnectionDetectionHandler extends SimpleChannelInboundHandler<String> {

    /**
     * 日志
     */
    private Logger log = LoggerFactory.getLogger(getClass());

    /**
     * 连接map
     */
    private ConcurrentHashMap<String, Integer> connectMap = new ConcurrentHashMap<>(16);

    /**
     * 连接数
     */
    private int connects;

    /**
     * 访问次数
     */
    private AttributeKey<Integer> accessNumKey = AttributeKey.valueOf("accessNum");

    /**
     * 时间差
     */
    private AttributeKey<Long> timeDifferenceKey = AttributeKey.valueOf("timeDifference");

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 最大连接数
        int maxConnects = NettyConfigUtils.maxConnects;
        String clientIp = NettyServerUtils.getClientIp(ctx);
        if (!connectMap.isEmpty()) {
            if (connectMap.containsKey(clientIp)) {
                connects++;
                if (connects > maxConnects) {
                    ctx.close();
                } else {
                    connectMap.put(clientIp, connects);
                }
            } else {
                connects++;
                connectMap.put(clientIp, connects);
            }
        } else {
            connects++;
            connectMap.put(clientIp, connects);
        }
        log.info("客户端[ip:" + clientIp + "]连接上");
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        // 连接一分钟最大访问数
        int maxAccessNum = NettyConfigUtils.maxAccessNum;
        Channel incoming = ctx.channel();
        // 访问次数
        Attribute<Integer> accessNumAttribute = incoming.attr(accessNumKey);
        Attribute<Long> timeDifferenceAttribute = incoming.attr(timeDifferenceKey);
        int accessNum = 1;
        long begin = System.currentTimeMillis();
        if (accessNumAttribute.get() == null) {
            accessNumAttribute.set(1);
        } else {
            accessNum = accessNumAttribute.get();
            accessNum++;
            accessNumAttribute.set(accessNum);
        }
        String clientIp = NettyServerUtils.getWholeClientIp(ctx);
        log.info(clientIp + "-第" + accessNum + "次请求");

        if (timeDifferenceAttribute.get() == null) {
            timeDifferenceAttribute.set(begin);
        } else {
            begin = timeDifferenceAttribute.get();
            long end = System.currentTimeMillis();
            long timeDifference = DateUtils.getMinuteDifference(begin, end);
            if (timeDifference >= 1) {
                accessNumAttribute.set(0);
                timeDifferenceAttribute.set(end);
                if (accessNum > maxAccessNum) {
                    ctx.close();
                }
            }
        }
        ctx.fireChannelRead(msg);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        connectMapProcess(ctx);
        ctx.fireChannelInactive();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        connectMapProcess(ctx);
        // 当出现异常就关闭连接
        ctx.close();
    }

    /**
     * 连接map处理
     * 
     * @param ctx
     */
    private void connectMapProcess(ChannelHandlerContext ctx) {
        String clientIp = NettyServerUtils.getClientIp(ctx);
        if (!connectMap.isEmpty()) {
            if (connectMap.containsKey(clientIp)) {
                connects = connectMap.get(clientIp).intValue();
                if (connects > 0) {
                    connects--;
                }
            }
        }
    }

}
